﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
/* wicknec@2012
 * 20121017 1.4 add create ssc file
 * 20121017 1.4 add convert to elphy compatible
 * 20121023 1.4 add rotation, align image
 * 20121023 1.4 add ssc file specification, todo: recover coordinates from ssc file.
 * 20130122 1.5 add move cursor function
 * 
 * */
namespace wicDIG
{
    public partial class mainWindow : Form
    {
        Bitmap miniMap;

        public mainWindow()
        {
            InitializeComponent();
            propertyGrid1.SelectedObject = new ProxyPreferences();
            
        }
        public void initCustom()
        {
            miniMap = new Bitmap(UtilDig.miniSize, UtilDig.miniSize);
        }
        public CoordStatus GetStatus()
        {
            return (propertyGrid1.SelectedObject as ProxyPreferences).CoordinateMode;
        }

        private void pic_DragDrop(object sender, DragEventArgs e)
        {
            if (e.Effect == DragDropEffects.All)
            {
                string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
                LoadPic(files[0]);
            }
        }
        public void LoadPic(string url)
        {
            pic.SizeMode = PictureBoxSizeMode.AutoSize;
            pic.Load(url);
            pic.SizeMode = PictureBoxSizeMode.StretchImage;
            UtilDig.zoomFactor = 100;
            Zoom();
            this.Text = "wicDIG - " + url;
            pic.ImageLocation = url;
            UtilDig.prevOnPic = new Point(-1, -1);
            MessageUpdate("File :" + url + " loaded");
        }
        public void SavePic(string url)
        {
            if (url == null || url == "")
                SaveAsPic();
            else
            {
                pic.Image.Save(url);
                MessageUpdate("File :" + url + " saved.");
            }
        }
        public void SaveAsPic()
        {
            SaveFileDialog sfd = new SaveFileDialog();
            sfd.DefaultExt = ".tif";
            DialogResult dr = sfd.ShowDialog();
            if (dr == DialogResult.OK)
            {
                pic.Image.Save(sfd.FileName);
                MessageUpdate("File " + pic.ImageLocation + " saved as :" + sfd.FileName + ".");
                pic.ImageLocation = sfd.FileName;
                this.Text = "wicDIG - " + pic.ImageLocation;
            }
            else
                WarningUpdate("file not saved.");
        }
        public void MessageUpdate(string msg)
        {
            toolStripStatusLabel1.Text = msg;
            toolStripStatusLabel1.ForeColor = Color.Green;
        }
        public void WarningUpdate(string warning)
        {
            toolStripStatusLabel1.Text = warning;
            toolStripStatusLabel1.ForeColor = Color.Green;
        }

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OpenFileDialog fOpen = new OpenFileDialog();
            DialogResult dr = fOpen.ShowDialog();
            if (dr == DialogResult.OK)
            {
                
                LoadPic(fOpen.FileName);
            }
            else
                WarningUpdate("File Open Operation Cancelled.");
        }
        /// <summary>
        /// set zoom by UtilDIG.zoomFactor and do the changes
        /// </summary>

        public void Zoom()
        {
            pic.SizeMode = PictureBoxSizeMode.StretchImage;
            pic.Size = new Size(pic.Image.Width * UtilDig.zoomFactor / 100, pic.Image.Height * UtilDig.zoomFactor / 100) ;
            
            pic.Update();
            MessageUpdate("Zoomed to " + UtilDig.zoomFactor.ToString() + " %") ;
            comboZoom.Text = UtilDig.zoomFactor.ToString() + "%";
            
        }

        private void pic_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop, false) == true)
                e.Effect = DragDropEffects.All;
        }

        private void quitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }
        public void CalcXy()
        {
            lbXyPic.Text = UtilDig.Point2String(UtilDig.nowOnPic);
            lbXyBoard.Text = UtilDig.Point2String(UtilDig.P2B(UtilDig.nowOnPic));
        }
        public void ShowInMini(Point onClient)
        {
            int s = UtilDig.miniSize;
            Point center = new Point(s/2,s/2);
            Graphics g = Graphics.FromImage(miniMap);


            //Rectangle lims = new Rectangle(UtilDig.PointOffset(onClient, -s * 50 / UtilDig.zoomFactor, -s * 50 / UtilDig.zoomFactor), new Size(s * 100 / UtilDig.zoomFactor, s * 100 / UtilDig.zoomFactor));
            Rectangle lims = new Rectangle(UtilDig.PointOffset(onClient, -s/2, -s/2), new Size(s, s));
            g.CopyFromScreen(UtilDig.PointOffset(pic.PointToScreen(onClient), -s/ 2, -s / 2), new Point(0, 0), new Size(s , s));
            g.DrawLine(UtilDig.myPen, UtilDig.PointOffset(center, -5, 0), UtilDig.PointOffset(center, 5, 0));
            g.DrawLine(UtilDig.myPen, UtilDig.PointOffset(center, 0, -5), UtilDig.PointOffset(center, 0, 5));

            MarkGrid(lims,g);
            picMini.Image = miniMap;
        }

        private void pic_MouseMove(object sender, MouseEventArgs e)
        {
            UtilDig.nowOnPic = new Point(e.Location.X * 100 / UtilDig.zoomFactor, e.Location.Y * 100 / UtilDig.zoomFactor);
            CalcXy();
            if (e.Button == MouseButtons.Left)
                ShowInMini(e.Location);
            else
                picMini.Image = null;
        }
        private void MarkPoint(int serial,Point onClient)
        {
            Graphics g;
            try
            {
                g = Graphics.FromImage(pic.Image);
            }
            catch (Exception e)
            {
                //most probably the image is indexed and graphics must be created a way around
                WarningUpdate(e.Message + ", the image is automatically converted.");
                Bitmap bm = new Bitmap(pic.Image);
                pic.Image.Dispose();
                pic.Image = bm;
                g = Graphics.FromImage(pic.Image);
                
                
            }
            string drawing = UtilDig.textFormat;
            drawing = drawing.Replace("%number%", serial.ToString());
            drawing = drawing.Replace("%coord%", UtilDig.Point2String(UtilDig.P2B(onClient)));
            drawing = drawing.Replace("%break%", "\r\n");

            g.DrawString(drawing, UtilDig.myFont, UtilDig.myBrush, onClient.X, onClient.Y);
            g.DrawLine(UtilDig.myPen, UtilDig.PointOffset(onClient, -5, 0), UtilDig.PointOffset(onClient, 5, 0));
            g.DrawLine(UtilDig.myPen, UtilDig.PointOffset(onClient, 0, -5), UtilDig.PointOffset(onClient, 0, 5));
            if (tbLine.Checked)
            {
                if (!UtilDig.prevOnPic.Equals(new Point(-1,-1)) && UtilDig.myPen.Width != 0)
                {
                    g.DrawLine(UtilDig.myPen, UtilDig.prevOnPic, onClient);
                    MessageUpdate("Line length : " + UtilDig.PointDistance(UtilDig.P2B(UtilDig.prevOnPic), UtilDig.P2B(onClient)).ToString());
                }
                UtilDig.prevOnPic = onClient;
            }
            pic.Refresh();
        }

        private void setCoordinatesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            UtilDig.Set1stCoordinate();
        }

        private void setSecondPointToolStripMenuItem_Click(object sender, EventArgs e)
        {
            UtilDig.Set2ndCoordinate();
        }

        private void setThirdPointToolStripMenuItem_Click(object sender, EventArgs e)
        {
            UtilDig.Set3rdCoordinate();
        }

        private void clearCoordinatesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            UtilDig.ClearCoordinates();
        }
        private void ClearData()
        {
            lstPoints.Items.Clear();
        }
        public int GetNextSerial()
        {
            return lstPoints.Items.Count + 1;
        }
        private int AddPoint(PointF onBoard)
        {
            int n = GetNextSerial();
            PointItem pi = new PointItem(n, onBoard);
            lstPoints.Items.Add(pi);
            txtCode.AppendText(pi.ToString()+"\r\n");
            return n;
        }

        private void clearDataToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ClearData();
            MessageUpdate("Point information cleared");
        }

        private void pic_DoubleClick(object sender, EventArgs e)
        {
            int n = AddPoint(UtilDig.P2B(UtilDig.nowOnPic));
            MarkPoint(n, UtilDig.nowOnPic);
        }

        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SavePic(pic.ImageLocation);
        }

        private void tbLine_Click(object sender, EventArgs e)
        {
            if (tbLine.Checked)
            {
                txtCode.AppendText("\\PolyLine\r\n\\width{" + UtilDig.myPen.Width.ToString() + "}\r\n");
            }
            else
            {
                txtCode.AppendText("\\endPolyLine\r\n");
                UtilDig.prevOnPic = new Point(-1, -1);
            }
        }

        private void tbColor_Click(object sender, EventArgs e)
        {
            ColorDialog cd = new ColorDialog();
            cd.AnyColor = true;
            cd.SolidColorOnly = false;
            DialogResult dr = cd.ShowDialog();
            if(dr == DialogResult.OK)
            {
                Color.FromArgb(200, cd.Color);
                UtilDig.ChangeColor(cd.Color);
            }
        }
        public void ChangeButtonColor(Color c)
        {
            tbColor.BackColor = c;
        }
        public void ChangeWidthSelection(float width)
        {
            foreach (ToolStripItem tsi in tbWidth.DropDownItems)
            {
                if (tsi is ToolStripMenuItem)
                    if (tsi.Text != width.ToString())
                        (tsi as ToolStripMenuItem).Checked = false;
                    else
                        (tsi as ToolStripMenuItem).Checked = true;
            }
                
        }
        private void toolStripMenuItem1_Click(object sender, EventArgs e)
        {
            UtilDig.ChangeWidth(1);
        }

        private void toolStripMenuItem2_Click(object sender, EventArgs e)
        {
            UtilDig.ChangeWidth(2);
        }

        private void toolStripMenuItem3_Click(object sender, EventArgs e)
        {
            UtilDig.ChangeWidth(3);
        }
        private void SaveText()
        {
            SaveFileDialog sfd = new SaveFileDialog();
            sfd.DefaultExt = ".txt";
            DialogResult dr = sfd.ShowDialog();
            if (dr == DialogResult.OK)
            {
                StreamWriter sw = new StreamWriter(sfd.FileName);
                sw.Write(txtCode.Text);
                sw.Close();
                MessageUpdate("Code file :" + sfd.FileName + " saved.");
            }
            else
                WarningUpdate("file not saved.");
        }

        private void saveDataToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SaveText();
        }

        private void toolStripMenuItem5_Click(object sender, EventArgs e)
        {
            UtilDig.ChangeWidth(0f);
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            wicDIGabout wda = new wicDIGabout();
            wda.ShowDialog();
        }

        private void loadDataToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.DefaultExt = ".txt";
            DialogResult dr = ofd.ShowDialog();
            if (dr == DialogResult.OK)
            {
                StreamReader sw = new StreamReader(ofd.FileName);
                txtCode.Text = sw.ReadToEnd();
                sw.Close();
                MessageUpdate("Code file :" + ofd.FileName + " loaded.");
            }
            else
                WarningUpdate("file not loaded.");
        }

        private void filteredCopyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Clipboard.SetText(txtCode.SelectedText.Replace("\r\n", ""));
            MessageUpdate("Selected text is copyed to clipboard.(" + Clipboard.GetText().Length.ToString() + ")");
        }

        private void pic_MouseDown(object sender, MouseEventArgs e)
        {

        }

        private void comboZoom_TextUpdate(object sender, EventArgs e)
        {
            try
            {
                if (comboZoom.Text.EndsWith("%"))
                    UtilDig.zoomFactor = int.Parse(comboZoom.Text.Substring(0, comboZoom.Text.Length - 1));
                else
                    UtilDig.zoomFactor = int.Parse(comboZoom.Text);
                Zoom();
            }
            catch (Exception ex)
            {
                WarningUpdate(ex.Message);
            }
        }

        private void pic_Click(object sender, EventArgs e)
        {
            pic.Focus();
        }

        private void connectLineToolStripMenuItem_Click(object sender, EventArgs e)
        {
            tbLine.Checked = !tbLine.Checked;
            tbLine_Click(sender, e);
        }

        private void comboZoom_KeyPress(object sender, KeyPressEventArgs e)
        {
        }

        private void btnInfo_Click(object sender, EventArgs e)
        {
            txtCode.AppendText(wicGeneral.FileInfo.GetFileName(pic.ImageLocation) + "\r\n");
        }

        private void generatesscFileForElphyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (pic.ImageLocation == null)
            {
                WarningUpdate("Please save the image first.");
                return;
            }
            string elphyTiffPath = pic.ImageLocation.Substring(0, pic.ImageLocation.LastIndexOf('.')) + ".tif";
            UtilDig.SaveElphyCompatible(pic.Image, elphyTiffPath);

            SscData data = new SscData();
            data["Bitmap"] = wicGeneral.FileInfo.GetFileName(elphyTiffPath);
            Point p = new Point(0, pic.Image.Size.Height);//lower left UV
            PointF pf = UtilDig.P2B(p);
            data["LowerLeftUV"] = string.Format("{0},{1}", new object[] { PointItem.Float2String(pf.X / 1000, 6), PointItem.Float2String(pf.Y / 1000, 6) });

            p = new Point(pic.Image.Size.Width, 0);//upper right UV
            pf = UtilDig.P2B(p);
            data["UpperRightUV"] = string.Format("{0},{1}", new object[] { PointItem.Float2String(pf.X / 1000, 6), PointItem.Float2String(pf.Y / 1000, 6) });
            p = new Point(0, 0);//upper right UV
            pf = UtilDig.P2B(p);
            //the upper left corner, seems to be invalid in Elphy, but can be used here to recover transformation settings
            data["LocalOriginUV"] = string.Format("{0},{1}", new object[] { PointItem.Float2String(pf.X / 1000, 6), PointItem.Float2String(pf.Y / 1000, 6) });
            //provided that the coordinate is nearly orthogonal
            double rAngle = UtilDig.GetAxesRotation();
            if (Math.Abs(rAngle) > 5)
            {
                DialogResult dr = MessageBox.Show(string.Format("Elphy Plus only support images with rotation zero. This image has rotation of {0} degrees, inaccuracy in locating will arise. Do you wish to continue?", new object[] { rAngle.ToString() }),
                    "Warning", MessageBoxButtons.OKCancel);
                if (dr == DialogResult.Cancel)
                    return;
            }
            data["RotationUV"] = rAngle.ToString();

            string saveFullFileName = pic.ImageLocation.Substring(0, pic.ImageLocation.LastIndexOf('.')) + ".ssc";

            SscInterface.Write(saveFullFileName, data);

            MessageUpdate("ssc File:" + saveFullFileName + " written. Image file converted.");
        }

        private void alignImageToolStripMenuItem_Click(object sender, EventArgs e)
        {
            float angle = UtilDig.GetAxesRotation();
            pic.Image = UtilDig.RotateImg(pic.Image, angle);
            MessageUpdate("Rotated " + angle.ToString() + " degrees counter-clockwise.");
            //generate new transformation
            Matrix trans = new Matrix(3);
            trans[0, 0] = trans[1, 1] = Math.Cos(angle * Math.PI / 180);
            trans[1, 0] = Math.Sin(angle * Math.PI / 180);
            trans[0, 1] = -trans[1, 0];
        }

        private void leftToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Point p = new Point();
            wic.UtilRobot.GetCursorPos(out p);
            UtilDig.Win.MessageUpdate(p.ToString());
            p.X = p.X - 1;
            wic.UtilRobot.SetCursorPos(p.X, p.Y) ;
        }

        private void rightToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Point p = new Point();
            wic.UtilRobot.GetCursorPos(out p);
            UtilDig.Win.MessageUpdate(p.ToString());
            p.X = p.X + 1;
            wic.UtilRobot.SetCursorPos(p.X, p.Y);
        }

        private void upToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Point p = new Point();
            wic.UtilRobot.GetCursorPos(out p);
            UtilDig.Win.MessageUpdate(p.ToString());
            p.Y = p.Y - 1;
            wic.UtilRobot.SetCursorPos(p.X, p.Y);
        }

        private void downToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Point p = new Point();
            wic.UtilRobot.GetCursorPos(out p);
            UtilDig.Win.MessageUpdate(p.ToString());
            p.Y = p.Y + 1;
            wic.UtilRobot.SetCursorPos(p.X, p.Y);
        }

        /// <summary>
        /// Mark Grid on minimap, as marking it on big map is difficult to implement
        /// </summary>
        /// <param name="lims">the limits in respect with picture</param>
        public void MarkGrid(Rectangle lims, Graphics g)
        {
            if (UtilDig.ShowGrid)
            {
                PointF start = UtilDig.P2B(new Point(lims.Left, lims.Top));
                PointF end = UtilDig.P2B(new Point(lims.Right, lims.Bottom));
                PointF p = new PointF(Math.Min(start.X, end.X), Math.Min(start.Y, end.Y));
                PointF blims = new PointF(Math.Max(start.X, end.X), Math.Max(start.Y, end.Y));
                float y = p.Y;
                Point dp;

                for (p.X = (float)Math.Round(p.X / UtilDig.RoundTo) * UtilDig.RoundTo; p.X < blims.X; p.X += UtilDig.RoundTo)
                    for (p.Y = (float)Math.Round(y / UtilDig.RoundTo) * UtilDig.RoundTo; p.Y < blims.Y; p.Y += UtilDig.RoundTo)
                    {
                        dp=UtilDig.PointOffset(UtilDig.B2P(p), -lims.Left, -lims.Top);//this is proportional to the viewing limit
                        //dp.X = dp.X * UtilDig.zoomFactor / 100;
                        //dp.Y = dp.Y * UtilDig.zoomFactor / 100;
                        DrawCross(dp, UtilDig.myPen, g, 3);
                    }
            }
        }
        private void DrawCross(Point pos, Pen pen, Graphics g, int size)
        {
            g.DrawLine(pen, UtilDig.PointOffset(pos, size, 0), UtilDig.PointOffset(pos,-size, 0));
            g.DrawLine(pen, UtilDig.PointOffset(pos, 0, -size), UtilDig.PointOffset(pos, 0, size));
        }

        private void stitchToolStripMenuItem_Click(object sender, EventArgs e)
        {
            pic.SizeMode = PictureBoxSizeMode.AutoSize;
            
            //
            OpenFileDialog ofd = new OpenFileDialog();
            if(pic.ImageLocation!= null && pic.ImageLocation.Length >0)
                ofd.InitialDirectory = wicGeneral.FileInfo.GetDirectory(pic.ImageLocation);
            ofd.Multiselect = true;
            ofd.Filter = "Slow Scan File|*.ssc|All File|*.*";
            DialogResult = ofd.ShowDialog();
            if (DialogResult != DialogResult.OK)
                return;

            //Determine the size of stitched image
            
            List<SscData> data = new List<SscData>();
            //load image data
            SscData d;
            foreach (string fn in ofd.FileNames)
            {
                if (fn.EndsWith(".ssc"))
                {
                    d = SscInterface.Read(fn);
                    d["src"] = wicGeneral.FileInfo.GetDirectory(fn) + d["Bitmap"];
                    data.Add(d);
                }
            }

            //determine field size
            PointF fll = SscInterface.DataString2Point(data[0]["LowerLeftUV"]);
            PointF fur = SscInterface.DataString2Point(data[0]["UpperRightUV"]);
            PointF tll = new PointF();
            PointF tur = new PointF();

            foreach (SscData sd in data)
            {
                tll = SscInterface.DataString2Point(sd["LowerLeftUV"]);
                tur = SscInterface.DataString2Point(sd["UpperRightUV"]);

                if (tll.X < fll.X)
                    fll.X = tll.X;
                if (tll.Y < fll.Y)
                    fll.Y = tll.Y;
                if (tur.X > fur.X)
                    fur.X = tur.X;
                if (tur.Y > fur.Y)
                    fur.Y = tur.Y;
            }

            //determine canvas size, using the resolution of the first picture, does not support rotation.
            Bitmap b = new Bitmap(Image.FromFile(data[0]["src"]));
            
            tll = SscInterface.DataString2Point(data[0]["LowerLeftUV"]);
            tur = SscInterface.DataString2Point(data[0]["UpperRightUV"]);

            //full width and height of the canvas
            int fw = (int)(b.Width * (fur.X - fll.X) / (tur.X - tll.X));
            int fh = (int)(b.Height * (fur.Y - fll.Y) / (tur.Y - tll.Y));
            
            //compute UV coordinate to canvas coordinate by (uvc-fll)/r;
            //grid density of the canvas: mm/px
            float rx = (tur.X - tll.X) / b.Width;
            float ry = (tur.Y - tll.Y) / b.Height;

            Bitmap canvas = new Bitmap(fw, fh);

            pic.Image = canvas;
            pic.SizeMode = PictureBoxSizeMode.StretchImage;

            //prepare to draw on the canvas
            Graphics g = Graphics.FromImage(canvas);
            
            //paint with background color
            g.FillRectangle(UtilDig.stitchBackground,new Rectangle(0,0,fw,fh));

            //reverse order to have a better view
            data.Reverse();

            //draw each picture
            foreach (SscData sd in data)
            {
                b = new Bitmap(Image.FromFile(sd["src"]));
                tll = SscInterface.DataString2Point(sd["LowerLeftUV"]);
                tur = SscInterface.DataString2Point(sd["UpperRightUV"]);

                g.DrawImage(b, new RectangleF((tll.X - fll.X) / rx, fh - (tur.Y - fll.Y) / ry, (tur.X - tll.X) / rx, (tur.Y - tll.Y) / ry));

                g.DrawString(sd["Bitmap"], UtilDig.myFont, UtilDig.myBrush, new PointF((tll.X - fll.X) / rx, fh - (tur.Y - fll.Y) / ry));
                g.Flush();
                pic.Refresh();
            }

            string url = data[0]["src"];
            url = wicGeneral.FileInfo.GetDirectory(url)+ UtilDig.stitchFileName;
            SavePic(url);
            pic.ImageLocation = url;

            this.Text = "wicDIG - " + url;
            //pic.ImageLocation = url;
            
            //reset point
            UtilDig.prevOnPic = new Point(-1, -1);

            //set overall transformation
            UtilDig.LastBCoords[0] = new PointF(fll.X*1000,fur.Y*1000);
            UtilDig.LastPCoords[0] = new Point(0, 0);

            UtilDig.LastBCoords[1] = new PointF(fll.X*1000,fll.Y*1000);
            UtilDig.LastPCoords[1] = new Point(0, fh);

            UtilDig.LastBCoords[2] = new PointF(fur.X * 1000, fur.Y * 1000);
            UtilDig.LastPCoords[2] = new Point(fw, 0);

            UtilDig.CoordSet[0] = true;
            UtilDig.CoordSet[1] = true;

            UtilDig.TrySetTrans();

            MessageUpdate("File :" + url + " loaded, stitch saved. Coordinate set.");
        }

    }
}
